//
//  HOMUtilities.m
//  HigherOrderMessaging
//
//  Created by Ofri Wolfus on 23/12/05.
//  Copyright 2005 Ofri Wolfus. All rights reserved.
//

#import "HOMUtilities.h"
#include <objc/objc-class.h>


//Renames originalSelector instance method of inClass to newSelector.
BOOL hom_renameInstnaceMethod(Class inClass, SEL originalSelector, SEL newSelector) {
	Method method = NULL;
	
	//Look for the method
	method = class_getInstanceMethod(inClass, originalSelector);
	if(method != NULL) {
		//Rename the method
		method->method_name = newSelector;
		return YES;
	}
	else
		return NO;
}

//Renames originalSelector class method of inClass to newSelector.
BOOL hom_renameClassMethod(Class inClass, SEL originalSelector, SEL newSelector) {
	Method method = NULL;
	
	//Look for the method
	method = class_getClassMethod(inClass, originalSelector);
	if(method != NULL) {
		//Rename the method
		method->method_name = newSelector;
		return YES;
	}
	else
		return NO;
}


//Rplaces the IMP of originalSelector instance method of classOne with the imp of newSelector of classTwo.
//Returns the original IMP of originalSelector.
IMP hom_replaceInstanceIMP(Class classOne, SEL originalSelector, Class classTwo, SEL newSelector) {
	Method	originalMethod = NULL;
	Method	newMethod = NULL;
	IMP		originalIMP = NULL;
	
	//Find the original method
	originalMethod = class_getInstanceMethod(classOne, originalSelector);
	//Find the new method
	newMethod = class_getInstanceMethod(classTwo, newSelector);
	
	if(originalMethod != NULL && newMethod != NULL) {
		//Save the original IMP
		originalIMP = originalMethod->method_imp;
		//Replace the IMPs
		originalMethod->method_imp = newMethod->method_imp;
	}
	
	return originalIMP;
}

//Rplaces the IMP of originalSelector class method of classOne with the imp of newSelector of classTwo.
//Returns the original IMP of originalSelector.
IMP hom_replaceClassIMP(Class classOne, SEL originalSelector, Class classTwo, SEL newSelector) {
	Method	originalMethod = NULL;
	Method	newMethod = NULL;
	IMP		originalIMP = NULL;
	
	//Find the original method
	originalMethod = class_getClassMethod(classOne, originalSelector);
	//Find the new method
	newMethod = class_getClassMethod(classTwo, newSelector);
	
	if(originalMethod != NULL && newMethod != NULL) {
		//Save the original IMP
		originalIMP = originalMethod->method_imp;
		//Replace the IMPs
		originalMethod->method_imp = newMethod->method_imp;
	}
	
	return originalIMP;
}


/***********************************************************************
* HOMSubtypeUntil.
*
* Delegation.
* Code taken from Apple's Objective-C runtime, released under ASPL.
**********************************************************************/
static int	HOMSubtypeUntil	       (const char *	type,
									char		end)
{
    int		level = 0;
    const char *	head = type;
	
    //
    while (*type)
    {
        if (!*type || (!level && (*type == end)))
            return (int)(type - head);
		
        switch (*type)
        {
            case ']': case '}': case ')': level--; break;
            case '[': case '{': case '(': level += 1; break;
        }
		
        type += 1;
    }
	
    //_objc_fatal ("Object: SubtypeUntil: end of type encountered prematurely\n");
    return 0;
}

/***********************************************************************
* HOMSkipFirstType.
* Code taken from Apple's Objective-C runtime, released under ASPL.
**********************************************************************/
static const char *	HOMSkipFirstType	   (const char *	type)
{
    while (1)
    {
        switch (*type++)
        {
            case 'O':	/* bycopy */
            case 'n':	/* in */
            case 'o':	/* out */
            case 'N':	/* inout */
            case 'r':	/* const */
            case 'V':	/* oneway */
            case '^':	/* pointers */
                break;
				
                /* arrays */
            case '[':
                while ((*type >= '0') && (*type <= '9'))
                    type += 1;
                return type + HOMSubtypeUntil (type, ']') + 1;
				
                /* structures */
            case '{':
                return type + HOMSubtypeUntil (type, '}') + 1;
				
                /* unions */
            case '(':
                return type + HOMSubtypeUntil (type, ')') + 1;
				
                /* basic types */
            default:
                return type;
        }
    }
}

const char * hom_getArgumentFromTypes(const char *types, unsigned index) {
	unsigned	i = 0;
	
	// First, skip the return type
	types = HOMSkipFirstType(types);
	
	// Next, skip stack size
    while ((*types >= '0') && (*types <= '9'))
        types += 1;
	
	for (i = 0; i < index; i++) {
		// Traverse argument type
        types = HOMSkipFirstType (types);
		
        // Skip GNU runtime's register parameter hint
        if (*types == '+') types++;
		
        // Traverse (possibly negative) argument offset
        if (*types == '-')
            types += 1;
        while ((*types >= '0') && (*types <= '9'))
            types += 1;
	}
	
	return types;
}

/*
 * Gets the type of an argument at a given index.
 * Modified from method_getNumberOfArguments() in Apple's Objective-C runtime.
 * Apple's runtime is released under the ASPL v2.0.
 */
/*const char * hom_getArgumentTypeAtIndex(Method m, unsigned index) {
	const char	*typedesc = m->method_types;
	unsigned	i = 0;
	
	// First, skip the return type
	typedesc = HOMSkipFirstType(typedesc);
	
	// Next, skip stack size
    while ((*typedesc >= '0') && (*typedesc <= '9'))
        typedesc += 1;
	
	for (i = 0; i < index; i++) {
		// Traverse argument type
        typedesc = HOMSkipFirstType (typedesc);
		
        // Skip GNU runtime's register parameter hint
        if (*typedesc == '+') typedesc++;
		
        // Traverse (possibly negative) argument offset
        if (*typedesc == '-')
            typedesc += 1;
        while ((*typedesc >= '0') && (*typedesc <= '9'))
            typedesc += 1;
	}
	
	return typedesc;
}*/

const char * hom_getArgumentTypeAtIndex(Method m, unsigned index) {
	return hom_getArgumentFromTypes(m->method_types, index);
}

char * hom_copyArgumentTypeAtIndex(Method m, unsigned index) {
	const char	*typedesc = hom_getArgumentTypeAtIndex(m, index);
	char		*type;
	const char	*end;
	
	end = HOMSkipFirstType (typedesc);
	type = malloc(end - typedesc + sizeof(char));
	strncpy(type, typedesc, sizeof(char) * (end - typedesc));
	
	return type;
}

int hom_getArgumentOffsetAtIndex(Method m, unsigned index) {
	return atoi(HOMSkipFirstType(hom_getArgumentTypeAtIndex(m, index)));
}

int hom_getArgumentOffsetFromTypes(const char *types, unsigned index) {
	return atoi(HOMSkipFirstType(hom_getArgumentFromTypes(types, index)));
}
